package com.zaodei.rxjob.job.service.impl;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.quartz.CronExpression;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.zaodei.rxjob.common.base.AjaxResult;
import com.zaodei.rxjob.common.utils.DateUtils;
import com.zaodei.rxjob.core.web.base.BaseController;
import com.zaodei.rxjob.job.admin.core.model.JobGroupProject;
import com.zaodei.rxjob.job.admin.core.model.XxlJobGroup;
import com.zaodei.rxjob.job.admin.core.model.XxlJobInfo;
import com.zaodei.rxjob.job.admin.core.route.ExecutorRouteStrategyEnum;
import com.zaodei.rxjob.job.admin.core.schedule.XxlJobDynamicScheduler;
import com.zaodei.rxjob.job.admin.core.util.I18nUtil;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
import com.xxl.job.core.glue.GlueTypeEnum;
import com.zaodei.rxjob.job.dao.ProjectJobGroupdao;
import com.zaodei.rxjob.job.dao.XxlJobGroupDao;
import com.zaodei.rxjob.job.dao.XxlJobInfoDao;
import com.zaodei.rxjob.job.dao.XxlJobLogDao;
import com.zaodei.rxjob.job.dao.XxlJobLogGlueDao;
import com.zaodei.rxjob.job.service.NXxlJobService;

/**
 * @Title:
 * @Description:
 * @Author:wuche@laozo.com
 * @time:2019年1月17日
 */
@Service
public class NXxlJobServiceImpl extends BaseController implements
        NXxlJobService {
    private static Logger logger = LoggerFactory
            .getLogger(NXxlJobServiceImpl.class);
    @Resource
    private XxlJobGroupDao xxlJobGroupDao;
    @Resource
    private XxlJobInfoDao xxlJobInfoDao;
    @Resource
    public XxlJobLogDao xxlJobLogDao;
    @Resource
    private XxlJobLogGlueDao xxlJobLogGlueDao;
     
    @Resource
    private ProjectJobGroupdao projectJobGroupdao;
    @Override
    public List<XxlJobInfo> selectJobInfoList(XxlJobInfo xxlJobInfo) {
        List<XxlJobInfo> list = xxlJobInfoDao.selectJobInfoList(xxlJobInfo);
        if (list != null && list.size() > 0) {
            for (XxlJobInfo jobInfo : list) {
                XxlJobDynamicScheduler.fillJobInfo(jobInfo);
            }
        }
        return list;
    }

    @Override
    public AjaxResult add(XxlJobInfo jobInfo) {
        // valid
        XxlJobGroup group = xxlJobGroupDao.load(jobInfo.getJobGroup());
        if (group == null) {
            return error(
                    (I18nUtil.getString("system_please_choose") + I18nUtil
                            .getString("jobinfo_field_jobgroup")));
        }
        if (!CronExpression.isValidExpression(jobInfo.getJobCron())) {
            return error(
                    I18nUtil.getString("jobinfo_field_cron_unvalid"));
        }
        if (StringUtils.isBlank(jobInfo.getJobDesc())) {
            return error(
                    (I18nUtil.getString("system_please_input") + I18nUtil
                            .getString("jobinfo_field_jobdesc")));
        }
        if (StringUtils.isBlank(jobInfo.getAuthor())) {
            return error(
                    (I18nUtil.getString("system_please_input") + I18nUtil
                            .getString("jobinfo_field_author")));
        }
        if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(),
                null) == null) {
            return error(
                    (I18nUtil.getString("jobinfo_field_executorRouteStrategy") + I18nUtil
                            .getString("system_unvalid")));
        }
        if (ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(),
                null) == null) {
            return error(
                    (I18nUtil.getString("jobinfo_field_executorBlockStrategy") + I18nUtil
                            .getString("system_unvalid")));
        }
        if (GlueTypeEnum.match(jobInfo.getGlueType()) == null) {
            return error(
                    (I18nUtil.getString("jobinfo_field_gluetype") + I18nUtil
                            .getString("system_unvalid")));
        }
        if (GlueTypeEnum.BEAN == GlueTypeEnum.match(jobInfo.getGlueType())
                && StringUtils.isBlank(jobInfo.getExecutorHandler())) {
            return error(
                    (I18nUtil.getString("system_please_input") + "JobHandler"));
        }

        // fix "\r" in shell
        if (GlueTypeEnum.GLUE_SHELL == GlueTypeEnum
                .match(jobInfo.getGlueType())
                && jobInfo.getGlueSource() != null) {
            jobInfo.setGlueSource(jobInfo.getGlueSource().replaceAll("\r", ""));
        }

        // ChildJobId valid
        if (StringUtils.isNotBlank(jobInfo.getChildJobId())) {
            String[] childJobIds = StringUtils.split(jobInfo.getChildJobId(),
                    ",");
            for (String childJobIdItem : childJobIds) {
                if (StringUtils.isNotBlank(childJobIdItem)
                        && StringUtils.isNumeric(childJobIdItem)) {
                    XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer
                            .valueOf(childJobIdItem));
                    if (childJobInfo == null) {
                        return error(
                                MessageFormat.format(
                                        (I18nUtil
                                                .getString("jobinfo_field_childJobId")
                                                + "({0})" + I18nUtil
                                                .getString("system_not_found")),
                                        childJobIdItem));
                    }
                } else {
                    return error(
                            MessageFormat.format(
                                    (I18nUtil
                                            .getString("jobinfo_field_childJobId")
                                            + "({0})" + I18nUtil
                                            .getString("system_unvalid")),
                                    childJobIdItem));
                }
            }
            jobInfo.setChildJobId(StringUtils.join(childJobIds, ","));
        }

        // add in db
        xxlJobInfoDao.save(jobInfo);
        if (jobInfo.getId() < 1) {
            return error(
                    (I18nUtil.getString("jobinfo_field_add") + I18nUtil
                            .getString("system_fail")));
        }
        return toAjax(true);
    }

    @Override
    public AjaxResult update(XxlJobInfo jobInfo) {

        // valid
        if (!CronExpression.isValidExpression(jobInfo.getJobCron())) {
            return error(
                    I18nUtil.getString("jobinfo_field_cron_unvalid"));
        }
        if (StringUtils.isBlank(jobInfo.getJobDesc())) {
            return error(
                    (I18nUtil.getString("system_please_input") + I18nUtil
                            .getString("jobinfo_field_jobdesc")));
        }
        if (StringUtils.isBlank(jobInfo.getAuthor())) {
            return error(
                    (I18nUtil.getString("system_please_input") + I18nUtil
                            .getString("jobinfo_field_author")));
        }
        if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(),
                null) == null) {
            return error(
                    (I18nUtil.getString("jobinfo_field_executorRouteStrategy") + I18nUtil
                            .getString("system_unvalid")));
        }
        if (ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(),
                null) == null) {
            return error(
                    (I18nUtil.getString("jobinfo_field_executorBlockStrategy") + I18nUtil
                            .getString("system_unvalid")));
        }

        // ChildJobId valid
        if (StringUtils.isNotBlank(jobInfo.getChildJobId())) {
            String[] childJobIds = StringUtils.split(jobInfo.getChildJobId(),
                    ",");
            for (String childJobIdItem : childJobIds) {
                if (StringUtils.isNotBlank(childJobIdItem)
                        && StringUtils.isNumeric(childJobIdItem)) {
                    XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer
                            .valueOf(childJobIdItem));
                    if (childJobInfo == null) {
                        return error(
                                MessageFormat.format(
                                        (I18nUtil
                                                .getString("jobinfo_field_childJobId")
                                                + "({0})" + I18nUtil
                                                .getString("system_not_found")),
                                        childJobIdItem));
                    }
                } else {
                    return error(
                            MessageFormat.format(
                                    (I18nUtil
                                            .getString("jobinfo_field_childJobId")
                                            + "({0})" + I18nUtil
                                            .getString("system_unvalid")),
                                    childJobIdItem));
                }
            }
            jobInfo.setChildJobId(StringUtils.join(childJobIds, ","));
        }

        // stage job info
        XxlJobInfo exists_jobInfo = xxlJobInfoDao.loadById(jobInfo.getId());
        if (exists_jobInfo == null) {
            return error(
                    (I18nUtil.getString("jobinfo_field_id") + I18nUtil
                            .getString("system_not_found")));
        }
        // String old_cron = exists_jobInfo.getJobCron();

        exists_jobInfo.setJobCron(jobInfo.getJobCron());
        exists_jobInfo.setJobDesc(jobInfo.getJobDesc());
        exists_jobInfo.setAuthor(jobInfo.getAuthor());
        exists_jobInfo.setAlarmEmail(jobInfo.getAlarmEmail());
        exists_jobInfo.setExecutorRouteStrategy(jobInfo
                .getExecutorRouteStrategy());
        exists_jobInfo.setExecutorHandler(jobInfo.getExecutorHandler());
        exists_jobInfo.setExecutorParam(jobInfo.getExecutorParam());
        exists_jobInfo.setExecutorBlockStrategy(jobInfo
                .getExecutorBlockStrategy());
        exists_jobInfo.setExecutorTimeout(jobInfo.getExecutorTimeout());
        exists_jobInfo.setExecutorFailRetryCount(jobInfo
                .getExecutorFailRetryCount());
        exists_jobInfo.setChildJobId(jobInfo.getChildJobId());
        xxlJobInfoDao.update(exists_jobInfo);
        String qz_group = String.valueOf(exists_jobInfo.getJobGroup());
        String qz_name = String.valueOf(exists_jobInfo.getId());
        try {
            XxlJobDynamicScheduler.updateJobCron(qz_group, qz_name,
                    exists_jobInfo.getJobCron());
        } catch (SchedulerException e) {
            logger.error(e.getMessage(), e);
            return toAjax(false);
        }

        return toAjax(true);
    }

    @Override
    public AjaxResult remove(int id) {
        XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(id);
        String group = String.valueOf(xxlJobInfo.getJobGroup());
        String name = String.valueOf(xxlJobInfo.getId());

        try {
            // unbind quartz
            XxlJobDynamicScheduler.removeJob(name, group);

            xxlJobInfoDao.delete(id);
            xxlJobLogDao.delete(id);
            xxlJobLogGlueDao.deleteByJobId(id);
            return toAjax(true);
        } catch (SchedulerException e) {
            logger.error(e.getMessage(), e);
            return toAjax(false);
        }

    }

    @Override
    public AjaxResult start(int id) {
        XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(id);
        String group = String.valueOf(xxlJobInfo.getJobGroup());
        String name = String.valueOf(xxlJobInfo.getId());
        String cronExpression = xxlJobInfo.getJobCron();

        try {
            boolean ret = XxlJobDynamicScheduler.addJob(name, group,
                    cronExpression);
            return ret ? toAjax(true) : toAjax(false);
        } catch (SchedulerException e) {
            logger.error(e.getMessage(), e);
            return toAjax(false);
        }
    }

    @Override
    public AjaxResult stop(int id) {
        XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(id);
        String group = String.valueOf(xxlJobInfo.getJobGroup());
        String name = String.valueOf(xxlJobInfo.getId());

        try {
            // bind quartz
            boolean ret = XxlJobDynamicScheduler.removeJob(name, group);
            return ret ? toAjax(true) : toAjax(false);
        } catch (SchedulerException e) {
            logger.error(e.getMessage(), e);
            return toAjax(false);
        }
    }

    /*
     * @Override public ReturnT<String> triggerJob(int id, int failRetryCount) {
     * JobTriggerPoolHelper.trigger(id, failRetryCount); return toAjax(true);
     *//*
        * XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(id); if (xxlJobInfo ==
        * null) { return error(
        * (I18nUtil.getString
        * ("jobinfo_field_id")+I18nUtil.getString("system_unvalid")) ); } String
        * group = String.valueOf(xxlJobInfo.getJobGroup()); String name =
        * String.valueOf(xxlJobInfo.getId()); try {
        * XxlJobDynamicScheduler.triggerJob(name, group); return
        * toAjax(true); } catch (SchedulerException e) {
        * logger.error(e.getMessage(), e); return new
        * ReturnT<String>(toAjax(false)_CODE, e.getMessage()); }
        *//*
           * }
           */

    @Override
    public Map<String, Object> dashboardInfo() {

        int jobInfoCount = xxlJobInfoDao.findAllCount();
        int jobLogCount = xxlJobLogDao.triggerCountByHandleCode(-1);
        int jobLogSuccessCount = xxlJobLogDao
                .triggerCountByHandleCode(ReturnT.SUCCESS_CODE);

        // executor count
        Set<String> executerAddressSet = new HashSet<String>();
        List<XxlJobGroup> groupList = xxlJobGroupDao.findAll();

        if (CollectionUtils.isNotEmpty(groupList)) {
            for (XxlJobGroup group : groupList) {
                if (CollectionUtils.isNotEmpty(group.getRegistryList())) {
                    executerAddressSet.addAll(group.getRegistryList());
                }
            }
        }

        int executorCount = executerAddressSet.size();

        Map<String, Object> dashboardMap = new HashMap<String, Object>();
        dashboardMap.put("jobInfoCount", jobInfoCount);
        dashboardMap.put("jobLogCount", jobLogCount);
        dashboardMap.put("jobLogSuccessCount", jobLogSuccessCount);
        dashboardMap.put("executorCount", executorCount);
        return dashboardMap;
    }


    @Override
    public ReturnT<Map<String, Object>> chartInfo(Date startDate, Date endDate) {
        /*
         * // get cache String cacheKey = TRIGGER_CHART_DATA_CACHE + "_" +
         * startDate.getTime() + "_" + endDate.getTime(); Map<String, Object>
         * chartInfo = (Map<String, Object>) LocalCacheUtil.get(cacheKey); if
         * (chartInfo != null) { return new ReturnT<Map<String,
         * Object>>(chartInfo); }
         */

        // process
        List<String> triggerDayList = new ArrayList<String>();
        List<Integer> triggerDayCountRunningList = new ArrayList<Integer>();
        List<Integer> triggerDayCountSucList = new ArrayList<Integer>();
        List<Integer> triggerDayCountFailList = new ArrayList<Integer>();
        int triggerCountRunningTotal = 0;
        int triggerCountSucTotal = 0;
        int triggerCountFailTotal = 0;

        List<Map<String, Object>> triggerCountMapAll = xxlJobLogDao
                .triggerCountByDay(startDate, endDate);
        if (CollectionUtils.isNotEmpty(triggerCountMapAll)) {
            for (Map<String, Object> item : triggerCountMapAll) {
                String day = String.valueOf(item.get("triggerDay"));
                int triggerDayCount = Integer.valueOf(String.valueOf(item
                        .get("triggerDayCount")));
                int triggerDayCountRunning = Integer.valueOf(String
                        .valueOf(item.get("triggerDayCountRunning")));
                int triggerDayCountSuc = Integer.valueOf(String.valueOf(item
                        .get("triggerDayCountSuc")));
                int triggerDayCountFail = triggerDayCount
                        - triggerDayCountRunning - triggerDayCountSuc;

                triggerDayList.add(day);
                triggerDayCountRunningList.add(triggerDayCountRunning);
                triggerDayCountSucList.add(triggerDayCountSuc);
                triggerDayCountFailList.add(triggerDayCountFail);

                triggerCountRunningTotal += triggerDayCountRunning;
                triggerCountSucTotal += triggerDayCountSuc;
                triggerCountFailTotal += triggerDayCountFail;
            }
        } else {
            for (int i = 4; i > -1; i--) {
                triggerDayList.add(FastDateFormat.getInstance("yyyy-MM-dd")
                        .format(DateUtils.addDays(new Date(), -i)));
                triggerDayCountRunningList.add(0);
                triggerDayCountSucList.add(0);
                triggerDayCountFailList.add(0);
            }
        }

        Map<String, Object> result = new HashMap<String, Object>();
        result.put("triggerDayList", triggerDayList);
        result.put("triggerDayCountRunningList", triggerDayCountRunningList);
        result.put("triggerDayCountSucList", triggerDayCountSucList);
        result.put("triggerDayCountFailList", triggerDayCountFailList);

        result.put("triggerCountRunningTotal", triggerCountRunningTotal);
        result.put("triggerCountSucTotal", triggerCountSucTotal);
        result.put("triggerCountFailTotal", triggerCountFailTotal);

        /*
         * // set cache LocalCacheUtil.set(cacheKey, result, 60*1000); // cache
         * 60s
         */

        return new ReturnT<Map<String, Object>>(result);
    }

    /**
     * 获取单条jobInfo
     */
    @Override
    public XxlJobInfo loadJobInfo(int id) {
    	XxlJobInfo  xxlJobInfo = xxlJobInfoDao.loadById(id);
    	JobGroupProject jobGroupProject =projectJobGroupdao.selectProjectJobGroup(xxlJobInfo.getJobGroup());
    	xxlJobInfo.setProjectId(jobGroupProject.getProjectId());
     return xxlJobInfo;
    }
    
}
